home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / dbus / proxies.py < prev    next >
Text File  |  2009-05-06  |  25KB  |  558 lines

  1. # Copyright (C) 2003-2007 Red Hat Inc. <http://www.redhat.com/>
  2. # Copyright (C) 2003 David Zeuthen
  3. # Copyright (C) 2004 Rob Taylor
  4. # Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
  5. #
  6. # Permission is hereby granted, free of charge, to any person
  7. # obtaining a copy of this software and associated documentation
  8. # files (the "Software"), to deal in the Software without
  9. # restriction, including without limitation the rights to use, copy,
  10. # modify, merge, publish, distribute, sublicense, and/or sell copies
  11. # of the Software, and to permit persons to whom the Software is
  12. # furnished to do so, subject to the following conditions:
  13. #
  14. # The above copyright notice and this permission notice shall be
  15. # included in all copies or substantial portions of the Software.
  16. #
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. # DEALINGS IN THE SOFTWARE.
  25.  
  26. import sys
  27. import logging
  28.  
  29. try:
  30.     from threading import RLock
  31. except ImportError:
  32.     from dummy_threading import RLock
  33.  
  34. import _dbus_bindings
  35. from dbus._expat_introspect_parser import process_introspection_data
  36. from dbus.exceptions import MissingReplyHandlerException, MissingErrorHandlerException, IntrospectionParserException, DBusException
  37.  
  38. __docformat__ = 'restructuredtext'
  39.  
  40.  
  41. _logger = logging.getLogger('dbus.proxies')
  42.  
  43. from _dbus_bindings import LOCAL_PATH, \
  44.                            BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE,\
  45.                            INTROSPECTABLE_IFACE
  46.  
  47.  
  48. class _DeferredMethod:
  49.     """A proxy method which will only get called once we have its
  50.     introspection reply.
  51.     """
  52.     def __init__(self, proxy_method, append, block):
  53.         self._proxy_method = proxy_method
  54.         # the test suite relies on the existence of this property
  55.         self._method_name = proxy_method._method_name
  56.         self._append = append
  57.         self._block = block
  58.  
  59.     def __call__(self, *args, **keywords):
  60.         if (keywords.has_key('reply_handler') or
  61.             keywords.get('ignore_reply', False)):
  62.             # defer the async call til introspection finishes
  63.             self._append(self._proxy_method, args, keywords)
  64.             return None
  65.         else:
  66.             # we're being synchronous, so block
  67.             self._block()
  68.             return self._proxy_method(*args, **keywords)
  69.  
  70.     def call_async(self, *args, **keywords):
  71.         self._append(self._proxy_method, args, keywords)
  72.  
  73.  
  74. class _ProxyMethod:
  75.     """A proxy method.
  76.  
  77.     Typically a member of a ProxyObject. Calls to the
  78.     method produce messages that travel over the Bus and are routed
  79.     to a specific named Service.
  80.     """
  81.     def __init__(self, proxy, connection, bus_name, object_path, method_name,
  82.                  iface):
  83.         if object_path == LOCAL_PATH:
  84.             raise DBusException('Methods may not be called on the reserved '
  85.                                 'path %s' % LOCAL_PATH)
  86.  
  87.         # trust that the proxy, and the properties it had, are OK
  88.         self._proxy          = proxy
  89.         self._connection     = connection
  90.         self._named_service  = bus_name
  91.         self._object_path    = object_path
  92.         # fail early if the method name is bad
  93.         _dbus_bindings.validate_member_name(method_name)
  94.         # the test suite relies on the existence of this property
  95.         self._method_name    = method_name
  96.         # fail early if the interface name is bad
  97.         if iface is not None:
  98.             _dbus_bindings.validate_interface_name(iface)
  99.         self._dbus_interface = iface
  100.  
  101.     def __call__(self, *args, **keywords):
  102.         reply_handler = keywords.pop('reply_handler', None)
  103.         error_handler = keywords.pop('error_handler', None)
  104.         ignore_reply = keywords.pop('ignore_reply', False)
  105.  
  106.         if reply_handler is not None or error_handler is not None:
  107.             if reply_handler is None:
  108.                 raise MissingReplyHandlerException()
  109.             elif error_handler is None:
  110.                 raise MissingErrorHandlerException()
  111.             elif ignore_reply:
  112.                 raise TypeError('ignore_reply and reply_handler cannot be '
  113.                                 'used together')
  114.  
  115.         dbus_interface = keywords.pop('dbus_interface', self._dbus_interface)
  116.  
  117.         if dbus_interface is None:
  118.             key = self._method_name
  119.         else:
  120.             key = dbus_interface + '.' + self._method_name
  121.         introspect_sig = self._proxy._introspect_method_map.get(key, None)
  122.  
  123.         if ignore_reply or reply_handler is not None:
  124.             self._connection.call_async(self._named_service,
  125.                                         self._object_path,
  126.                                         dbus_interface,
  127.                                         self._method_name,
  128.                                         introspect_sig,
  129.                                         args,
  130.                                         reply_handler,
  131.                                         error_handler,
  132.                                         **keywords)
  133.         else:
  134.             return self._connection.call_blocking(self._named_service,
  135.                                                   self._object_path,
  136.                                                   dbus_interface,
  137.                                                   self._method_name,
  138.                                                   introspect_sig,
  139.                                                   args,
  140.                                                   **keywords)
  141.  
  142.     def call_async(self, *args, **keywords):
  143.         reply_handler = keywords.pop('reply_handler', None)
  144.         error_handler = keywords.pop('error_handler', None)
  145.  
  146.         dbus_interface = keywords.pop('dbus_interface', self._dbus_interface)
  147.  
  148.         if dbus_interface:
  149.             key = dbus_interface + '.' + self._method_name
  150.         else:
  151.             key = self._method_name
  152.         introspect_sig = self._proxy._introspect_method_map.get(key, None)
  153.  
  154.         self._connection.call_async(self._named_service,
  155.                                     self._object_path,
  156.                                     dbus_interface,
  157.                                     self._method_name,
  158.                                     introspect_sig,
  159.                                     args,
  160.                                     reply_handler,
  161.                                     error_handler,
  162.                                     **keywords)
  163.  
  164.  
  165. class ProxyObject(object):
  166.     """A proxy to the remote Object.
  167.  
  168.     A ProxyObject is provided by the Bus. ProxyObjects
  169.     have member functions, and can be called like normal Python objects.
  170.     """
  171.     ProxyMethodClass = _ProxyMethod
  172.     DeferredMethodClass = _DeferredMethod
  173.  
  174.     INTROSPECT_STATE_DONT_INTROSPECT = 0
  175.     INTROSPECT_STATE_INTROSPECT_IN_PROGRESS = 1
  176.     INTROSPECT_STATE_INTROSPECT_DONE = 2
  177.  
  178.     def __init__(self, conn=None, bus_name=None, object_path=None,
  179.                  introspect=True, follow_name_owner_changes=False, **kwargs):
  180.         """Initialize the proxy object.
  181.  
  182.         :Parameters:
  183.             `conn` : `dbus.connection.Connection`
  184.                 The bus or connection on which to find this object.
  185.                 The keyword argument `bus` is a deprecated alias for this.
  186.             `bus_name` : str
  187.                 A bus name for the application owning the object, to be used
  188.                 as the destination for method calls and the sender for
  189.                 signal matches. The keyword argument ``named_service`` is a
  190.                 deprecated alias for this.
  191.             `object_path` : str
  192.                 The object path at which the application exports the object
  193.             `introspect` : bool
  194.                 If true (default), attempt to introspect the remote
  195.                 object to find out supported methods and their signatures
  196.             `follow_name_owner_changes` : bool
  197.                 If true (default is false) and the `bus_name` is a
  198.                 well-known name, follow ownership changes for that name
  199.         """
  200.         bus = kwargs.pop('bus', None)
  201.         if bus is not None:
  202.             if conn is not None:
  203.                 raise TypeError('conn and bus cannot both be specified')
  204.             conn = bus
  205.             from warnings import warn
  206.             warn('Passing the bus parameter to ProxyObject by name is '
  207.                  'deprecated: please use positional parameters',
  208.                  DeprecationWarning, stacklevel=2)
  209.         named_service = kwargs.pop('named_service', None)
  210.         if named_service is not None:
  211.             if bus_name is not None:
  212.                 raise TypeError('bus_name and named_service cannot both be '
  213.                                 'specified')
  214.             bus_name = named_service
  215.             from warnings import warn
  216.             warn('Passing the named_service parameter to ProxyObject by name '
  217.                  'is deprecated: please use positional parameters',
  218.                  DeprecationWarning, stacklevel=2)
  219.         if kwargs:
  220.             raise TypeError('ProxyObject.__init__ does not take these '
  221.                             'keyword arguments: %s'
  222.                             % ', '.join(kwargs.iterkeys()))
  223.  
  224.         if follow_name_owner_changes:
  225.             # we don't get the signals unless the Bus has a main loop
  226.             # XXX: using Bus internals
  227.             conn._require_main_loop()
  228.  
  229.         self._bus = conn
  230.  
  231.         if bus_name is not None:
  232.             _dbus_bindings.validate_bus_name(bus_name)
  233.         # the attribute is still called _named_service for the moment,
  234.         # for the benefit of telepathy-python
  235.         self._named_service = self._requested_bus_name = bus_name
  236.  
  237.         _dbus_bindings.validate_object_path(object_path)
  238.         self.__dbus_object_path__ = object_path
  239.  
  240.         if not follow_name_owner_changes:
  241.             self._named_service = conn.activate_name_owner(bus_name)
  242.  
  243.         #PendingCall object for Introspect call
  244.         self._pending_introspect = None
  245.         #queue of async calls waiting on the Introspect to return
  246.         self._pending_introspect_queue = []
  247.         #dictionary mapping method names to their input signatures
  248.         self._introspect_method_map = {}
  249.  
  250.         # must be a recursive lock because block() is called while locked,
  251.         # and calls the callback which re-takes the lock
  252.         self._introspect_lock = RLock()
  253.  
  254.         if not introspect or self.__dbus_object_path__ == LOCAL_PATH:
  255.             self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
  256.         else:
  257.             self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS
  258.  
  259.             self._pending_introspect = self._Introspect()
  260.  
  261.     bus_name = property(lambda self: self._named_service, None, None,
  262.             """The bus name to which this proxy is bound. (Read-only,
  263.             may change.)
  264.  
  265.             If the proxy was instantiated using a unique name, this property
  266.             is that unique name.
  267.  
  268.             If the proxy was instantiated with a well-known name and with
  269.             ``follow_name_owner_changes`` set false (the default), this
  270.             property is the unique name of the connection that owned that
  271.             well-known name when the proxy was instantiated, which might
  272.             not actually own the requested well-known name any more.
  273.  
  274.             If the proxy was instantiated with a well-known name and with
  275.             ``follow_name_owner_changes`` set true, this property is that
  276.             well-known name.
  277.             """)
  278.  
  279.     requested_bus_name = property(lambda self: self._requested_bus_name,
  280.             None, None,
  281.             """The bus name which was requested when this proxy was
  282.             instantiated.
  283.             """)
  284.  
  285.     object_path = property(lambda self: self.__dbus_object_path__,
  286.             None, None,
  287.             """The object-path of this proxy.""")
  288.  
  289.     # XXX: We don't currently support this because it's the signal receiver
  290.     # that's responsible for tracking name owner changes, but it
  291.     # seems a natural thing to add in future.
  292.     #unique_bus_name = property(lambda self: something, None, None,
  293.     #        """The unique name of the connection to which this proxy is
  294.     #        currently bound. (Read-only, may change.)
  295.     #        """)
  296.  
  297.     def connect_to_signal(self, signal_name, handler_function, dbus_interface=None, **keywords):
  298.         """Arrange for the given function to be called when the given signal
  299.         is received.
  300.  
  301.         :Parameters:
  302.             `signal_name` : str
  303.                 The name of the signal
  304.             `handler_function` : callable
  305.                 A function to be called when the signal is emitted by
  306.                 the remote object. Its positional arguments will be the
  307.                 arguments of the signal; optionally, it may be given
  308.                 keyword arguments as described below.
  309.             `dbus_interface` : str
  310.                 Optional interface with which to qualify the signal name.
  311.                 If None (the default) the handler will be called whenever a
  312.                 signal of the given member name is received, whatever
  313.                 its interface.
  314.         :Keywords:
  315.             `utf8_strings` : bool
  316.                 If True, the handler function will receive any string
  317.                 arguments as dbus.UTF8String objects (a subclass of str
  318.                 guaranteed to be UTF-8). If False (default) it will receive
  319.                 any string arguments as dbus.String objects (a subclass of
  320.                 unicode).
  321.             `byte_arrays` : bool
  322.                 If True, the handler function will receive any byte-array
  323.                 arguments as dbus.ByteArray objects (a subclass of str).
  324.                 If False (default) it will receive any byte-array
  325.                 arguments as a dbus.Array of dbus.Byte (subclasses of:
  326.                 a list of ints).
  327.             `sender_keyword` : str
  328.                 If not None (the default), the handler function will receive
  329.                 the unique name of the sending endpoint as a keyword
  330.                 argument with this name
  331.             `destination_keyword` : str
  332.                 If not None (the default), the handler function will receive
  333.                 the bus name of the destination (or None if the signal is a
  334.                 broadcast, as is usual) as a keyword argument with this name.
  335.             `interface_keyword` : str
  336.                 If not None (the default), the handler function will receive
  337.                 the signal interface as a keyword argument with this name.
  338.             `member_keyword` : str
  339.                 If not None (the default), the handler function will receive
  340.                 the signal name as a keyword argument with this name.
  341.             `path_keyword` : str
  342.                 If not None (the default), the handler function will receive
  343.                 the object-path of the sending object as a keyword argument
  344.                 with this name
  345.             `message_keyword` : str
  346.                 If not None (the default), the handler function will receive
  347.                 the `dbus.lowlevel.SignalMessage` as a keyword argument with
  348.                 this name.
  349.             `arg...` : unicode or UTF-8 str
  350.                 If there are additional keyword parameters of the form
  351.                 ``arg``\ *n*, match only signals where the *n*\ th argument
  352.                 is the value given for that keyword parameter. As of this time
  353.                 only string arguments can be matched (in particular,
  354.                 object paths and signatures can't).
  355.         """
  356.         return \
  357.         self._bus.add_signal_receiver(handler_function,
  358.                                       signal_name=signal_name,
  359.                                       dbus_interface=dbus_interface,
  360.                                       bus_name=self._named_service,
  361.                                       path=self.__dbus_object_path__,
  362.                                       **keywords)
  363.  
  364.     def _Introspect(self):
  365.         return self._bus.call_async(self._named_service,
  366.                                     self.__dbus_object_path__,
  367.                                     INTROSPECTABLE_IFACE, 'Introspect', '', (),
  368.                                     self._introspect_reply_handler,
  369.                                     self._introspect_error_handler,
  370.                                     utf8_strings=True,
  371.                                     require_main_loop=False)
  372.  
  373.     def _introspect_execute_queue(self):
  374.         # FIXME: potential to flood the bus
  375.         # We should make sure mainloops all have idle handlers
  376.         # and do one message per idle
  377.         for (proxy_method, args, keywords) in self._pending_introspect_queue:
  378.             proxy_method(*args, **keywords)
  379.  
  380.     def _introspect_reply_handler(self, data):
  381.         self._introspect_lock.acquire()
  382.         try:
  383.             try:
  384.                 self._introspect_method_map = process_introspection_data(data)
  385.             except IntrospectionParserException, e:
  386.                 self._introspect_error_handler(e)
  387.                 return
  388.  
  389.             self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_DONE
  390.             self._pending_introspect = None
  391.             self._introspect_execute_queue()
  392.         finally:
  393.             self._introspect_lock.release()
  394.  
  395.     def _introspect_error_handler(self, error):
  396.         logging.basicConfig()
  397.         _logger.error("Introspect error on %s:%s: %s.%s: %s",
  398.                       self._named_service, self.__dbus_object_path__,
  399.                       error.__class__.__module__, error.__class__.__name__,
  400.                       error)
  401.         self._introspect_lock.acquire()
  402.         try:
  403.             _logger.debug('Executing introspect queue due to error')
  404.             self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
  405.             self._pending_introspect = None
  406.             self._introspect_execute_queue()
  407.         finally:
  408.             self._introspect_lock.release()
  409.  
  410.     def _introspect_block(self):
  411.         self._introspect_lock.acquire()
  412.         try:
  413.             if self._pending_introspect is not None:
  414.                 self._pending_introspect.block()
  415.             # else someone still has a _DeferredMethod from before we
  416.             # finished introspection: no need to do anything special any more
  417.         finally:
  418.             self._introspect_lock.release()
  419.  
  420.     def _introspect_add_to_queue(self, callback, args, kwargs):
  421.         self._introspect_lock.acquire()
  422.         try:
  423.             if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
  424.                 self._pending_introspect_queue.append((callback, args, kwargs))
  425.             else:
  426.                 # someone still has a _DeferredMethod from before we
  427.                 # finished introspection
  428.                 callback(*args, **kwargs)
  429.         finally:
  430.             self._introspect_lock.release()
  431.  
  432.     def __getattr__(self, member):
  433.         if member.startswith('__') and member.endswith('__'):
  434.             raise AttributeError(member)
  435.         else:
  436.             return self.get_dbus_method(member)
  437.  
  438.     def get_dbus_method(self, member, dbus_interface=None):
  439.         """Return a proxy method representing the given D-Bus method. The
  440.         returned proxy method can be called in the usual way. For instance, ::
  441.  
  442.             proxy.get_dbus_method("Foo", dbus_interface='com.example.Bar')(123)
  443.  
  444.         is equivalent to::
  445.  
  446.             proxy.Foo(123, dbus_interface='com.example.Bar')
  447.  
  448.         or even::
  449.  
  450.             getattr(proxy, "Foo")(123, dbus_interface='com.example.Bar')
  451.  
  452.         However, using `get_dbus_method` is the only way to call D-Bus
  453.         methods with certain awkward names - if the author of a service
  454.         implements a method called ``connect_to_signal`` or even
  455.         ``__getattr__``, you'll need to use `get_dbus_method` to call them.
  456.  
  457.         For services which follow the D-Bus convention of CamelCaseMethodNames
  458.         this won't be a problem.
  459.         """
  460.  
  461.         ret = self.ProxyMethodClass(self, self._bus,
  462.                                     self._named_service,
  463.                                     self.__dbus_object_path__, member,
  464.                                     dbus_interface)
  465.  
  466.         # this can be done without taking the lock - the worst that can
  467.         # happen is that we accidentally return a _DeferredMethod just after
  468.         # finishing introspection, in which case _introspect_add_to_queue and
  469.         # _introspect_block will do the right thing anyway
  470.         if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
  471.             ret = self.DeferredMethodClass(ret, self._introspect_add_to_queue,
  472.                                            self._introspect_block)
  473.  
  474.         return ret
  475.  
  476.     def __repr__(self):
  477.         return '<ProxyObject wrapping %s %s %s at %#x>'%(
  478.             self._bus, self._named_service, self.__dbus_object_path__, id(self))
  479.     __str__ = __repr__
  480.  
  481.  
  482. class Interface(object):
  483.     """An interface into a remote object.
  484.  
  485.     An Interface can be used to wrap ProxyObjects
  486.     so that calls can be routed to their correct
  487.     D-Bus interface.
  488.     """
  489.  
  490.     def __init__(self, object, dbus_interface):
  491.         """Construct a proxy for the given interface on the given object.
  492.  
  493.         :Parameters:
  494.             `object` : `dbus.proxies.ProxyObject` or `dbus.Interface`
  495.                 The remote object or another of its interfaces
  496.             `dbus_interface` : str
  497.                 An interface the `object` implements
  498.         """
  499.         if isinstance(object, Interface):
  500.             self._obj = object.proxy_object
  501.         else:
  502.             self._obj = object
  503.         self._dbus_interface = dbus_interface
  504.  
  505.     object_path = property (lambda self: self._obj.object_path, None, None,
  506.                             "The D-Bus object path of the underlying object")
  507.     __dbus_object_path__ = object_path
  508.     bus_name = property (lambda self: self._obj.bus_name, None, None,
  509.                          "The bus name to which the underlying proxy object "
  510.                          "is bound")
  511.     requested_bus_name = property (lambda self: self._obj.requested_bus_name,
  512.                                    None, None,
  513.                                    "The bus name which was requested when the "
  514.                                    "underlying object was created")
  515.     proxy_object = property (lambda self: self._obj, None, None,
  516.                              """The underlying proxy object""")
  517.     dbus_interface = property (lambda self: self._dbus_interface, None, None,
  518.                                """The D-Bus interface represented""")
  519.  
  520.     def connect_to_signal(self, signal_name, handler_function,
  521.                           dbus_interface=None, **keywords):
  522.         """Arrange for a function to be called when the given signal is
  523.         emitted.
  524.  
  525.         The parameters and keyword arguments are the same as for
  526.         `dbus.proxies.ProxyObject.connect_to_signal`, except that if
  527.         `dbus_interface` is None (the default), the D-Bus interface that
  528.         was passed to the `Interface` constructor is used.
  529.         """
  530.         if not dbus_interface:
  531.             dbus_interface = self._dbus_interface
  532.  
  533.         return self._obj.connect_to_signal(signal_name, handler_function,
  534.                                            dbus_interface, **keywords)
  535.  
  536.     def __getattr__(self, member):
  537.         if member.startswith('__') and member.endswith('__'):
  538.             raise AttributeError(member)
  539.         else:
  540.             return self._obj.get_dbus_method(member, self._dbus_interface)
  541.  
  542.     def get_dbus_method(self, member, dbus_interface=None):
  543.         """Return a proxy method representing the given D-Bus method.
  544.  
  545.         This is the same as `dbus.proxies.ProxyObject.get_dbus_method`
  546.         except that if `dbus_interface` is None (the default),
  547.         the D-Bus interface that was passed to the `Interface` constructor
  548.         is used.
  549.         """
  550.         if dbus_interface is None:
  551.             dbus_interface = self._dbus_interface
  552.         return self._obj.get_dbus_method(member, dbus_interface)
  553.  
  554.     def __repr__(self):
  555.         return '<Interface %r implementing %r at %#x>'%(
  556.         self._obj, self._dbus_interface, id(self))
  557.     __str__ = __repr__
  558.